home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume2
/
dos
/
fs
/
backup20.1
next >
Wrap
Text File
|
1988-11-05
|
51KB
|
2,188 lines
Path: xanth!nic.MR.NET!hal!cwjcc!mailrus!ulowell!page
From: page@swan.ulowell.edu (Bob Page)
Newsgroups: comp.sources.amiga
Subject: v02i052: backup - file backup utility V2.01
Message-ID: <10049@swan.ulowell.edu>
Date: 4 Nov 88 22:20:16 GMT
Organization: University of Lowell, Computer Science Dept.
Lines: 2177
Approved: page@swan.ulowell.edu
Submitted-by: dillon@postgres.berkeley.edu (Matt Dillon)
Posting-number: Volume 2, Issue 52
Archive-name: dos/fs/backup201.1
There are some serious bugs in backup/restore version V2.00.
This is version 2.01, and fixes the following problems:
(1) Restore would not put files in the proper sub-directory
(2) Append mode for backup does not work
These bugs were found by Jan Sven Trabandt.
# This is a shell archive.
# Remove everything above and including the cut line.
# Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
# Run the following text with /bin/sh to create:
# backup.c
# Makefile
# backup.doc
# This archive created: Fri Nov 4 17:11:15 1988
cat << \SHAR_EOF > backup.c
/*
* BACKUP.C
*
* (C)Copyright 1986-88, Matthew Dillon, All Rights Reserved.
* Permission is granted to distribute for non-profit only.
*
* Thanks to Jan Sven Trabandt for finding some major bugs!
*
* This program will backup a filesystem or directory, creating a single
* output file which can later be RESTORE'd from. It is a quick way to
* backup your work to a 'backup' disk.
*
* backup [options] path path path ... path [-ooutputfile]
*
* NOTE: if -o is not specified, not output file will be created
*
* options:
*
* -A ARCHIVE. Clear the archive bit on backed up files
*
* -U UPDATE. Backup only those files which have the archive bit
* cleared.
*
* -f[#KB] Floppy... actually, this option is used to force the backup
* program to automatically split up the backup files in #KB
* sections (default 800). I.E. -f with nothing else will
* use 800KB chunks. -f200 would use 200KB chunks, etc...
*
* PLEASE SEE THE DOCS FOR MORE INFORMATION ON FLOPPY BACKUP
* AND RESTORE
*
* -Fvol example: -FDF0: -FDF1: This command specifies the ordering
* and number of (floppy) drives to backup to. This allows
* one to change floppies in one drive while it is backing up
* to another. It automatically cycles through the drives but
* you still must specify an initial output file (-ofile) to
* determine the base name for the files.
*
* This command also forces user prompting.
*
* PLEASE SEE THE DOCS FOR MORE INFORMATION ON FLOPPY BACKUP
* AND RESTORE
*
* -b backup (default if executable name is 'backup')
*
* -r restore (default if executable nam is 'restore')
*
* -a append to the destination file.
*
* -c Compress files during backup
*
* -s Only display directories as we go along.
*
* -l/-t (either option) Causes a RESTORE to only LIST the files,
* not do an actual restore.
*
* -T (Restore) TIMESTAMP, COMMENT, AND PROTECTION BITS ONLY.
* NO files are created. It is assumed the files already
* exist. The timestamp and other fields are transfered
* from the backup file to the already-restored files (useful
* if the files were restored with a copy command that did
* non copy the timestamps. Even if the backup file is old,
* you can recover most of your time data).
*
* -S Silent. Don't display files or directories.
*
* -nKB Set buffer size to use, in KB.
*
* -v Verbose... Additionaly, display those files which will NOT
* be backed up.
*
* -pPAT only file-paths matching this pattern are backed up. You
* may specify more than one '-p' option.
*
* -dPAT file-paths matching this pattern are NOT backed up. you
* may specify more than one '-d' option.
*
* -ofile Output File
*
* ---------------------------------------------------------------------
*
* destination file format:
*
* HDR = <HDR><N.B><datestamp> -Backup Date
* VOL = <VOL><name_size.B><name> -VOLUME base
* DDS = <DDS><name_size.B><name> -down-directory
* END = <END><0.B> -up directory
* DAT = <DAT><N.B><datestamp> -datestamp for file
* PRO = <PRO><4.B><protection> -protection for file
* COM = <COM><N.B><comment> -comment for file
* FIL0= <FIL0><N.B><name><size.L><data> -uncompressed file
* FIL1= <FIL1><N.B><name><size.L><usize.L><data> -compressed form #1
* INC = <INC><12.B><ttlsize><strt><segsize> -next file is part of an
* incomplete file
*/
#include <stdio.h>
#include <fcntl.h>
#include <local/typedefs.h>
#define SDIR struct _SDIR
#define SCOMP struct _SCOMP
SCOMP {
MNODE Node;
uword Bytes; /* allocated bytes */
uword N;
};
SDIR {
MNODE Node; /* node in dir tree */
ubyte Type; /* XDDS or XVOL */
ubyte HaveFile; /* Something was backed up in here */
char *Element; /* path element */
};
#define XVOL 0x01
#define XDDS 0x02
#define XEND 0x03
#define XDAT 0x04
#define XPRO 0x05
#define XCOM 0x06
#define XFIL0 0x87
#define XFIL1 0x88
#define XHDR 0x09
#define XINC 0x0A
ubyte Break;
ubyte Restore;
ubyte ListOnly;
ubyte ShowFiles = 1;
ubyte ShowDirs = 1;
ubyte Verbose;
ubyte Archive;
ubyte Update;
ubyte Append;
ubyte Compress;
ubyte TimeStampOnly;
char *OutFile;
long BacBytes;
short BacCnt = 1;
uword NumPicks;
uword NumDels;
char *Picks[32];
char *Dels[32];
char DirPath[256];
short DPLen;
long BufSize = 65536;
long BufI;
ubyte *Buf;
long InBufSize = 8192;
long InBufI, InBufN;
ubyte *InBuf;
MLIST VList; /* Volume list (-F), of NODEs */
MLIST DList; /* Directory Stack */
long CLen; /* Actual compressed file output length */
MLIST CList; /* List of memory buffers */
SCOMP *CWrite; /* Current memory buffer pointer */
extern int Enable_Abort;
extern void seekinputend();
extern FIB *GetFileInfo();
extern SCOMP *NewSComp();
extern void *malloc(), *GetHead(), *GetTail(), *GetSucc(), *GetPred();
main(ac, av)
char *av[];
{
register short i, notdone;
register char *str;
NewList(&VList);
NewList(&DList);
NewList(&CList);
Enable_Abort = 0;
for (str = av[0] + strlen(av[0]); str >= av[0] && *str != '/' && *str != ':'; --str);
++str;
if ((*str|0x20) == 'r')
Restore = 1;
if (ac == 1) {
printf("Backup/Restore V2.01, (c)Copyright 1988 Matthew Dillon, All Rights Reserved\n", str);
printf("%s -rbactlvASTU -d<pat> -p<pat> -f[#kb] -F<vol> -n<#kb> -ofile\n", str);
}
for (i = 1; i < ac; ++i) {
str = av[i];
if (*str != '-')
continue;
notdone = 1;
++str;
while (notdone && *str) {
switch(*str) {
case 'r':
Restore = 1;
break;
case 'b':
Restore = 0;
break;
case 'a':
Append = 1;
break;
case 'c':
Compress = 1;
break;
case 'd':
Dels[NumDels++] = str + 1;
notdone = 0;
break;
case 'f':
BacBytes = 800 * 1024;
if (str[1] >= '0' && str[1] <= '9') {
BacBytes = atoi(str+1) * 1024;
notdone = 0;
}
break;
case 'F':
{ /* strlen(str+1)+1 */
register NODE *node = malloc(sizeof(NODE)+strlen(str));
node->ln_Name = (char *)(node+1);
strcpy(node+1, str+1);
AddTail(&VList, node);
}
notdone = 0;
break;
case 'n':
BufSize = atoi(str+1) * 1024;
if (BufSize <= 0)
BufSize = 65536;
notdone = 0;
break;
case 'o':
OutFile = str + 1;
notdone = 0;
break;
case 'p':
Picks[NumPicks++] = str + 1;
notdone = 0;
break;
case 's':
ShowFiles = 0;
break;
case 't':
case 'l':
ListOnly = 1;
break;
case 'v':
Verbose= 1;
break;
case 'A':
Archive= 1;
break;
case 'S':
ShowFiles = 0;
ShowDirs = 0;
break;
case 'T':
TimeStampOnly = 1;
break;
case 'U':
Update = 1;
break;
default:
puts("failure");
exit(20);
}
++str;
}
}
Buf = malloc(BufSize);
if (Buf == NULL) {
printf("Unable to malloc %ld bytes\n", BufSize);
exit(20);
}
if (ListOnly)
InBufSize = 512; /* small buffer to avoid read overhead */
/* since we are skipping the meat */
InBuf = malloc(InBufSize);
if (InBuf == NULL) {
printf("Unable to malloc %ld bytes\n", InBufSize);
exit(20);
}
if (Restore)
RestoreFiles(ac,av);
else
BackupFiles(ac,av);
}
long SaveLock;
BackupFiles(ac, av)
char *av[];
{
register short i;
register char *str, *ptr;
char notdone;
if (OutFile && openoutput(OutFile, Append, ((BacBytes)?1:0)) == 0)
exit(20);
if (OutFile) { /* write header */
DATESTAMP Date;
DateStamp(&Date);
outentry(XHDR, sizeof(DATESTAMP), &Date);
}
SaveLock = CurrentDir(DupLock(((PROC *)FindTask(NULL))->pr_CurrentDir));
for (i = 1; i < ac; ++i) {
str = av[i];
if (*str == '-')
continue;
/*
* Push DDS entries for each name segment of the path
*/
notdone = 1;
while (notdone) {
for (ptr = str; *ptr && *ptr != ':' && *ptr != '/'; ++ptr);
switch(*ptr) {
case '/': /* normal directory */
*ptr = 0;
PushDir(str, XDDS);
str = ptr + 1;
*ptr = '/';
break;
case ':': /* volume */
*ptr = 0;
PushDir(str, XVOL);
str = ptr + 1;
*ptr = ':';
break;
default: /* directory or file */
{
char *path = av[i];
FIB *fib;
long lock;
if (fib = GetFileInfo(path, &lock)) {
if (fib->fib_DirEntryType > 0) {
if (str[0])
PushDir(str, XDDS);
lock = scan_directory(fib, lock);
if (str[0])
PopDirs(1);
} else {
lock = scan_file(fib, lock);
}
FreeFileInfo(fib, lock);
} else {
printf("Unable to get info for %s\n", av[i]);
}
}
notdone = 0;
break;
}
}
PopDirs(-1);
}
UnLock(CurrentDir(SaveLock));
if (OutFile)
closeoutput();
}
DATESTAMP Date;
char Comment[256];
char Scr[256];
long Protection;
long IncSize; /* Size of entire file */
long IncSeek; /* Seek offset into file */
long IncLen; /* # bytes in this segment */
RestoreFiles(ac, av)
char *av[];
{
register short i;
register char *str;
char notdone;
char havedate;
char havepro;
char havecom;
char haveinc;
long bytes;
long actual;
long lock;
SaveLock = CurrentDir(lock = DupLock(((PROC *)FindTask(NULL))->pr_CurrentDir));
for (i = 1; i < ac; ++i) {
str = av[i];
if (*str == '-')
continue;
if (openinput(str) == 0) {
printf("Unable to open %s for input\n", str);
continue;
}
notdone = 1;
havedate = havepro = havecom = haveinc = 0;
while (notdone) {
short c = oreadchar();
short l = oreadchar();
switch(c) {
case -1:
notdone = 0;
break;
case XVOL:
oread(Scr, l);
Scr[l] = 0;
if (OutFile) { /* Restore to OutFile instead */
register short j = strlen(OutFile);
strcpy(Scr, OutFile);
if (j && OutFile[j-1] == '/') {
c = XDDS;
Scr[j-1] = 0;
} else if (j && OutFile[j-1] == ':') {
c = XVOL;
Scr[j-1] = 0;
} else
c = XDDS;
}
PushDir(Scr, c);
if (ListOnly)
break;
lock = Lock(DirPath, SHARED_LOCK); /* DirPath incs ':' */
if (lock == NULL && c == XDDS) {
if (lock = CreateDir(Scr)) /* Scr excludes '/' */
UnLock(lock);
lock = Lock(Scr, SHARED_LOCK);
}
{
SDIR *sd = GetTail(&DList);
sd->HaveFile = 1; /* don't remove dir */
}
if (lock == NULL) {
printf("Unable to create directory %s\n", Scr);
notdone = 0;
} else {
UnLock(CurrentDir(lock));
}
break;
case XDDS:
oread(Scr, l);
Scr[l] = 0;
PushDir(Scr, XDDS);
if (ListOnly)
break;
lock = Lock(Scr, SHARED_LOCK);
if (lock == NULL) {
if (lock = CreateDir(Scr))
UnLock(lock);
lock = Lock(Scr, SHARED_LOCK);
} else {
SDIR *sd = GetTail(&DList);
sd->HaveFile = 1; /* don't remove dir */
}
if (lock == NULL) {
printf("Unable to create directory %s\n", Scr);
notdone = 0;
} else {
UnLock(CurrentDir(lock));
}
break;
case XEND:
{
SDIR *sd = GetTail(&DList);
ubyte type;
c = 1;
if (!sd)
break;
type = sd->Type;
strcpy(Scr, sd->Element);
c = PopDirs(1);
if (ListOnly)
break;
if (type == XVOL) /* no parent directory */
break;
lock = ParentDir(lock);
if (lock == NULL) {
puts("Unable to ParentDir!");
notdone = 0;
} else {
if (c == 0)
DeleteFile(Scr);
UnLock(CurrentDir(lock));
}
}
break;
case XDAT:
if (l != sizeof(DATESTAMP)) {
puts("expected sizeof datestamp");
notdone = 0;
break;
}
oread(&Date, l);
havedate = 1;
break;
case XPRO:
if (l != 4) {
puts("Expected 4 bytes for protection");
notdone = 0;
break;
}
oread(&Protection, l);
havepro = 1;
break;
case XCOM:
oread(Comment, l);
Comment[l] = 0;
havecom = 1;
break;
case XFIL0:
case XFIL1:
if (!havepro)
Protection = 0;
if (!havecom)
Comment[0] = 0;
if (!havedate)
DateStamp(&Date);
if (!haveinc)
IncSize = 0;
oread(Scr, l);
Scr[l] = 0;
oread(&bytes, 4); /* length of file */
actual = bytes;
if (c == XFIL1) {
oread(&actual, 4);
bytes -= 4;
}
setinputbound(bytes);
{
short res = read_file(c, Scr, bytes, actual);
seekinputend();
if (res < 0)
goto bend;
}
if (ListOnly)
goto bend;
if (Archive)
SetProtection(Scr, Protection|FIBF_ARCHIVE);
else
SetProtection(Scr, Protection&~FIBF_ARCHIVE);
if (havedate)
setfiledate(Scr, &Date);
if (havecom && Comment[0])
SetComment(Scr, Comment);
bend:
havecom = havedate = havepro = haveinc = 0;
break;
case XHDR:
if (l != sizeof(DATESTAMP)) {
puts("expected sizeof datestamp");
notdone = 0;
break;
}
oread(&Date, l);
printf(" ----- BACKUP ----- BACKUP DATE: %s\n", datetos(&Date, Scr, NULL));
break;
case XINC:
if (l != 12) {
puts("expected 12 bytes for XINC");
notdone = 0;
break;
}
oread(&IncSize, 4);
oread(&IncSeek, 4);
oread(&IncLen, 4);
haveinc = 1;
break;
default:
printf("Unknown Record Type: %02x\n", c);
notdone = 0;
break;
}
setinputbound(-1);
if (mycheckbreak()) {
Break = 1;
notdone = 0;
break;
}
}
if (Break)
break;
}
UnLock(CurrentDir(SaveLock));
}
FIB *
GetFileInfo(path, plock)
char *path;
long *plock;
{
register long lock;
register FIB *fib;
*plock = NULL;
if (lock = Lock(path, SHARED_LOCK)) {
if (fib = malloc(sizeof(FIB))) {
if (Examine(lock, fib)) {
*plock = lock;
return(fib);
}
free(fib);
}
UnLock(lock);
}
return(NULL);
}
FreeFileInfo(fib, lock)
FIB *fib;
long lock;
{
if (fib)
free(fib);
if (lock)
UnLock(lock);
}
PushDir(element, type)
char *element;
{
register SDIR *sd = malloc(sizeof(SDIR));
register char *str = malloc(strlen(element)+1);
strcpy(str, element);
sd->Type = type;
sd->HaveFile = 0;
sd->Element = str;
AddTail(&DList, sd);
strcat(DirPath+DPLen, str);
if (type == XVOL)
strcat(DirPath+DPLen, ":");
else
strcat(DirPath+DPLen, "/");
DPLen += strlen(DirPath+DPLen);
}
PopDirs(num)
uword num;
{
register SDIR *sd, *sp;
char lasthave = 0;
while (num && (sd = GetTail(&DList))) {
lasthave |= sd->HaveFile;
if (sd->HaveFile) /* MUST write end-block */
outentry(XEND, 0, NULL);
if (sp = GetPred(sd))
sp->HaveFile |= sd->HaveFile;
Remove(sd);
DPLen -= strlen(sd->Element) + 1;
if (DPLen < 0) {
puts("DPLEN ERROR");
DPLen = 0;
}
DirPath[DPLen] = 0;
free(sd->Element);
free(sd);
--num;
}
return(lasthave);
}
/*
* SCAN_DIRECTORY() (CORE OF BACKUP)
*/
scan_directory(dirfib, dirlock)
FIB *dirfib;
long dirlock;
{
register FIB *fib;
long lock;
long save = CurrentDir(dirlock);
while (ExNext(dirlock, dirfib) && (fib = GetFileInfo(dirfib->fib_FileName, &lock))) {
if (fib->fib_DirEntryType > 0) {
PushDir(fib->fib_FileName, XDDS);
if (ShowDirs)
printf("%-40s (DIR)\n", DirPath);
lock = scan_directory(fib, lock);
PopDirs(1);
} else {
lock = scan_file(fib, lock);
}
FreeFileInfo(fib, lock);
if (Break || mycheckbreak()) {
Break = 1;
break;
}
}
CurrentDir(save);
return(dirlock);
}
mycheckbreak()
{
if (SetSignal(0, (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) & (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)) {
puts(" ***** BREAK *****");
return(1);
}
return(0);
}
/*
* SCAN_FILE()
*
* If the file is accepted, write out pending directory entries, do
* compression if any, and write out the file.
*/
scan_file(fib, lock)
FIB *fib;
long lock;
{
long save;
long n;
char dbuf[32];
strcat(DirPath, fib->fib_FileName);
if (Update && (fib->fib_Protection & FIBF_ARCHIVE))
goto nomatch;
{
register short i;
for (i = 0; i < NumPicks; ++i) {
if (wildcmp(Picks[i], DirPath))
break;
}
if (i && i == NumPicks)
goto nomatch;
for (i = 0; i < NumDels; ++i) {
if (wildcmp(Dels[i], DirPath))
goto nomatch;
}
}
if (ShowFiles)
printf("%-40s %6ld %s\n", DirPath, fib->fib_Size, datetos(&fib->fib_Date, dbuf, NULL));
{
register SDIR *sd;
SDIR *sdb = NULL;
for (sd = GetTail(&DList); sd; sd = GetPred(sd)) {
if (sd->HaveFile == 0)
sdb = sd;
}
for (sd = sdb; sd; sd = GetSucc(sd)) {
sd->HaveFile = 1;
outentry(sd->Type, strlen(sd->Element), sd->Element);
}
}
if (OutFile) {
save = CurrentDir(lock);
if (openinput("") == 0) {
CurrentDir(save);
printf("Unable to open %s\n", fib->fib_FileName);
goto nomatch;
}
if (Compress && CompressFile(fib->fib_FileName, fib->fib_Size)) {
if (OutFile && BacBytes && outbytes() + CLen > BacBytes) {
if (newfile() == 0)
goto skip1;
}
writeheaders(fib);
outentry(XFIL1, strlen(fib->fib_FileName), fib->fib_FileName);
CLen += 4;
owrite(&CLen, 4);
CLen -= 4;
owrite(&fib->fib_Size, 4);
transfer1(fib->fib_Size);
} else {
if (OutFile && BacBytes && outbytes() + fib->fib_Size > BacBytes) {
if (newfile() == 0)
goto skip1;
}
if (Compress)
rollbackinput();
writeheaders(fib);
outentry(XFIL0, strlen(fib->fib_FileName), fib->fib_FileName);
owrite(&fib->fib_Size, 4);
transfer0(fib->fib_Size);
}
skip1:
closeinput();
CurrentDir(save);
}
if (Break)
goto nomatch;
if (Archive && !(fib->fib_Protection & FIBF_ARCHIVE)) {
if (save = ParentDir(lock)) {
UnLock(lock);
save = CurrentDir(save);
SetProtection(fib->fib_FileName, fib->fib_Protection | FIBF_ARCHIVE);
lock = CurrentDir(save);
}
}
DirPath[DPLen] = 0;
return(lock);
nomatch:
if (Verbose)
printf("%-40s (NOT ACCEPTED)\n", DirPath, fib->fib_Size, datetos(&fib->fib_Date, dbuf, NULL));
DirPath[DPLen] = 0;
return(lock);
}
writeheaders(fib)
register FIB *fib;
{
outentry(XDAT, sizeof(DATESTAMP), &fib->fib_Date);
outentry(XPRO, 4, &fib->fib_Protection);
if (fib->fib_Comment[0])
outentry(XCOM, strlen(fib->fib_Comment), fib->fib_Comment);
}
/*
* (1) Write out XEND's to finish this archive,
* (2) Open a new output file
* (3) Write out XDDS's to build back to the current position
*/
newfile()
{
{
register SDIR *sd;
for (sd = GetTail(&DList); sd; sd = GetPred(sd)) {
if (sd->HaveFile)
outentry(XEND, 0, NULL);
}
}
++BacCnt;
if (OutFile && openoutput(OutFile, Append, 1) == 0) {
Break = 1;
return(0);
}
{
register SDIR *sd;
DATESTAMP Date;
DateStamp(&Date);
outentry(XHDR, sizeof(DATESTAMP), &Date);
for (sd = GetHead(&DList); sd; sd = GetSucc(sd)) {
if (sd->HaveFile)
outentry(sd->Type, strlen(sd->Element), sd->Element);
}
}
return(1);
}
read_file(type, fname, inbytes, outbytes)
short type;
char *fname;
{
char dbuf[32];
strcat(DirPath, fname);
{
register short i;
for (i = 0; i < NumPicks; ++i) {
if (wildcmp(Picks[i], DirPath))
break;
}
if (i && i == NumPicks) {
if (Verbose)
printf("%-40s (NOT ACCEPTED)\n", DirPath);
goto nomatch;
}
for (i = 0; i < NumDels; ++i) {
if (wildcmp(Dels[i], DirPath)) {
if (Verbose)
printf("%-40s (NOT ACCEPTED)\n", DirPath);
goto nomatch;
}
}
}
printf("%-40s %6ld %6ld %s %s\n", DirPath, inbytes, outbytes, datetos(&Date, dbuf, NULL), Comment);
if (ListOnly)
goto nomatch;
if (TimeStampOnly)
goto matchskip;
openoutput(fname, 0, 0);
switch(type) {
case XFIL0:
transfer0(inbytes);
break;
case XFIL1:
UnCompressFile(inbytes);
transfer1(outbytes);
break;
}
closeoutput();
matchskip:
DirPath[DPLen] = 0;
return(1);
nomatch:
DirPath[DPLen] = 0;
return(-1);
}
/*
* FILE SUPPORT
*/
static int Infd = -1;
static int Outfd = -1;
static long OutBytes;
openoutput(name, append, enabtail)
char *name;
{
char *ptr = name;
static NODE *VNode; /* Volume node */
long lock;
extern int errno;
if (Outfd >= 0) {
dumpoutput();
close(Outfd);
Outfd = -1;
}
if (enabtail) {
if (VNode)
VNode = GetSucc(VNode);
if (!VNode)
VNode = GetHead(&VList);
if (VNode) {
ptr = malloc(strlen(VNode->ln_Name)+strlen(name)+8);
sprintf(ptr, "%s%s.%02ld", VNode->ln_Name, name, BacCnt);
} else {
ptr = malloc(strlen(name)+8);
sprintf(ptr, "%s.%02ld", name, BacCnt);
}
}
OutBytes = 0;
while (GetHead(&VList)) {
short c;
short d;
fprintf(stderr, "Ready for %s (y=go,n=abort) -", ptr);
fflush(stderr);
if ((c = getc(stdin)) == EOF) {
fprintf(stderr, "EOF, aborted\n");
c = 'n';
}
while ((d = getc(stdin)) != EOF && d != '\n');
if ((c|0x20) == 'y')
break;
if ((c|0x20) == 'n')
goto skip;
}
if (enabtail && SaveLock) /* original directory */
lock = CurrentDir(SaveLock);
if (append) {
Outfd = open(ptr, O_WRONLY|O_CREAT|O_APPEND);
if (Outfd >= 0) {
OutBytes = lseek(Outfd, 0L, 2);
if (!append)
lseek(Outfd, 0L, 0);
}
} else {
Outfd = open(ptr, O_WRONLY|O_CREAT|O_TRUNC);
}
if (enabtail && SaveLock) /* back to before */
CurrentDir(lock);
if (Outfd < 0)
printf("Unable to open output file %s (%ld)\n", ptr, errno);
skip:
BufI = 0;
if (enabtail)
free(ptr);
return(Outfd >= 0);
}
oputc(v)
char v;
{
++OutBytes;
if (Outfd >= 0) {
if (BufI == BufSize)
dumpoutput();
Buf[BufI++] = v;
}
}
owrite(buf, n)
register char *buf;
register long n;
{
register long avail;
OutBytes += n;
if (Outfd >= 0) {
while (BufI + n > BufSize) {
avail = BufSize - BufI;
bmov(buf, Buf + BufI, avail);
n -= avail;
buf+= avail;
BufI = BufSize;
dumpoutput();
}
bmov(buf, Buf + BufI, n);
BufI += n;
}
}
dumpoutput()
{
if (Outfd >= 0 && BufI) {
write(Outfd, Buf, BufI);
BufI = 0;
}
}
closeoutput()
{
if (Outfd >= 0) {
dumpoutput();
close(Outfd);
Outfd = -1;
}
}
outbytes()
{
return(OutBytes);
}
/*
* <type><len><buf>
*/
outentry(type, len, buf)
ubyte type;
ubyte len;
ubyte *buf;
{
OutBytes += len + 2;
if (Outfd >= 0) {
if (BufI + len + 2 >= BufSize)
dumpoutput();
Buf[BufI+0] = type;
Buf[BufI+1] = len;
bmov(buf, Buf+BufI+2, len);
BufI += len + 2;
}
}
ulong OMax;
openinput(name)
char *name;
{
if (Infd >= 0)
close(Infd);
Infd = open(name, O_RDONLY);
InBufI = InBufN = 0;
OMax = -1;
return(Infd >= 0);
}
closeinput()
{
if (Infd >= 0)
close(Infd);
Infd = -1;
}
void
seekinputend()
{
register long inbuf = InBufI - InBufN;
register long forward = OMax;
if (forward > inbuf) {
lseek(Infd, forward - inbuf, 1);
OMax = InBufI = InBufN = 0;
return;
}
InBufN += forward;
}
setinputbound(max)
{
OMax = max;
}
oread(buf, n)
char *buf;
long n;
{
long x = 0;
long avail;
if (Infd < 0)
return(0);
if (n > OMax)
n = OMax;
while (n > (avail = InBufI - InBufN)) {
if (InBufN == -1)
return(0);
bmov(InBuf + InBufN, buf, avail);
OMax-= avail;
n -= avail;
buf += avail;
x += avail;
InBufI = read(Infd, InBuf, InBufSize);
InBufN = 0;
if (InBufI <= 0) {
InBufI = 0;
return(x);
}
}
bmov(InBuf + InBufN, buf, n);
InBufN += n;
x += n;
OMax -= n;
return(x);
}
oreadchar()
{
if (!OMax || Infd < 0)
return(-1);
if (InBufN == InBufI) {
if (InBufN < 0)
return(EOF);
InBufI = read(Infd, InBuf, InBufSize);
InBufN = 0;
if (InBufI == 0) {
InBufN = InBufI = -1;
return(-1);
}
}
return(InBuf[InBufN++]);
}
rollbackinput()
{
if (Infd >= 0)
lseek(Infd, 0L, 0);
InBufI = InBufN = 0;
}
mputc(v)
char v;
{
register SCOMP *sc = CWrite;
++CLen;
if (sc->N == sc->Bytes) {
sc = GetSucc(sc);
if (sc == NULL);
sc = NewSComp();
if (sc == NULL) {
puts("SCOMP FAILED");
return(0);
}
sc->N = 0;
CWrite = sc;
}
((char *)(sc + 1))[sc->N++] = v;
}
mwrite(buf, n)
char *buf;
long n;
{
register SCOMP *sc = CWrite;
register long avail;
CLen += n;
while ((avail = sc->Bytes - sc->N) < n) {
bmov(buf, (char *)(sc + 1) + sc->N, avail);
buf += avail;
n -= avail;
sc->N = sc->Bytes;
sc = GetSucc(sc);
if (sc == NULL)
sc = NewSComp();
if (sc == NULL) {
puts("SCOMP FAILED");
return(0);
}
sc->N = 0;
}
bmov(buf, (char *)(sc + 1) + sc->N, n);
sc->N += n;
CWrite = sc;
}
SCOMP *
NewSComp()
{
register SCOMP *sc = malloc(sizeof(SCOMP) + 8192);
if (sc) {
sc->Bytes = 8192;
sc->N = 0;
AddTail(&CList, sc);
}
return(sc);
}
transfer0(n)
long n;
{
register long len;
if (Outfd < 0)
return(n);
for (len = BufSize - BufI; n; len = BufSize - BufI) {
if (len == 0) {
dumpoutput();
len = BufSize;
}
if (n < len)
len = n;
oread(Buf + BufI, len);
BufI += len;
n -= len;
OutBytes += len;
}
}
/*
* Compression Routines
*
* transfer1(n) : Backup: copy compression buffer to output file
*/
transfer1(a)
{
register long len;
register SCOMP *sc = GetHead(&CList);
register ubyte *ptr;
long n = CLen;
if (Outfd < 0)
return(n);
for (sc = GetHead(&CList); sc && n; sc = GetSucc(sc)) {
len = sc->Bytes;
ptr = (ubyte *)(sc + 1);
if (n < len)
len = n;
n -= len;
while (len > BufSize - BufI) {
bmov(ptr, Buf + BufI, BufSize - BufI);
ptr += BufSize - BufI;
len -= BufSize - BufI;
OutBytes += BufSize - BufI;
BufI = BufSize;
dumpoutput();
}
bmov(ptr, Buf + BufI, len);
BufI += len;
OutBytes += len;
}
if (n)
puts("Unexpected EOF in compression file");
}
#asm
; Taken from my DRES.LIBRARY so we don't have to open the
; library.
public _GetHead
public _GetTail
public _GetSucc
public _GetPred
_GetSucc:
_GetHead: move.l 4(sp),A0
move.l (A0),A0
tst.l (A0)
bne .gh1
.ghz sub.l A0,A0
.gh1 move.l A0,D0
rts
_GetTail: move.l 4(sp),A0
move.l 8(A0),A0
tst.l 4(A0)
beq .ghz
move.l A0,D0
rts
_GetPred: move.l 4(sp),A0
move.l 4(A0),A0
tst.l 4(A0)
beq .ghz
move.l A0,D0
rts
#endasm
#define ngetchar() oreadchar()
#define nputchar(n) mputc(n)
#ifndef min
#define min(a,b) ((a>b) ? b : a)
#endif
#define BITS 13
#if BITS == 16
#define HSIZE 69001 /* 95% occupancy */
#endif
#if BITS == 15
#define HSIZE 35023 /* 94% occupancy */
#endif
#if BITS == 14
#define HSIZE 18013 /* 91% occupancy */
#endif
#if BITS == 13
#define HSIZE 9001 /* 91% occupancy */
#endif
#if BITS <= 12
#define HSIZE 5003 /* 80% occupancy */
#endif
typedef long code_int;
typedef long count_int;
typedef unsigned char char_type;
#define MAXCODE(n_bits) ((1 << (n_bits)) - 1)
#define INIT_BITS 9 /* initial number of bits/code */
int n_bits; /* number of bits/code */
int maxbits; /* user settable max # bits/code */
code_int maxcode; /* maximum code, given n_bits */
code_int maxmaxcode; /* should NEVER generate this code */
count_int htab[HSIZE];
uword codetab[HSIZE];
#define htabof(i) htab[i]
#define codetabof(i) codetab[i]
code_int hsize = HSIZE; /* for dynamic table sizing */
#define tab_prefixof(i) codetabof(i)
#define tab_suffixof(i) ((char_type *)(htab))[i]
#define de_stack ((char_type *)&tab_suffixof(1<<BITS))
code_int free_ent; /* first unused entry */
code_int getcode();
#define CHECK_GAP 10000 /* ratio check interval */
int block_compress = 1;
int clear_flg;
long ratio;
count_int checkpoint;
/*
* the next two codes should not be changed lightly, as they must not
* lie within the contiguous general code space.
*/
#define FIRST 257 /* first free entry */
#define CLEAR 256 /* table clear output code */
static int offset;
long int in_count = 1; /* length of input */
/*
* Compress a file to memory-buffers and return TRUE if the compressed
* size is smaller than the actual size.
*/
CompressFile(name, fsize)
{
long fcode;
code_int i = 0;
int c;
code_int ent;
int disp;
code_int hsize_reg;
int hshift;
if (wildcmp("*.Z", name) || wildcmp("*.ARC", name) || wildcmp("*.ZOO", name)) {
printf(" Will not compress %s\n", name);
return(0);
}
CLen = 0;
CWrite = GetHead(&CList);
if (CWrite == NULL)
CWrite = NewSComp();
CWrite->N = 0;
bzero(htab, sizeof(htab));
bzero(codetab, sizeof(codetab));
hsize = HSIZE;
if ( fsize < (1 << 12) )
hsize = min ( 5003, HSIZE );
else if ( fsize < (1 << 13) )
hsize = min ( 9001, HSIZE );
else if ( fsize < (1 << 14) )
hsize = min ( 18013, HSIZE );
else if ( fsize < (1 << 15) )
hsize = min ( 35023, HSIZE );
else if ( fsize < 47000 )
hsize = min ( 50021, HSIZE );
offset = clear_flg = ratio = 0;
in_count = 1;
checkpoint = CHECK_GAP;
n_bits = INIT_BITS; /* number of bits/code */
maxbits = BITS; /* user settable max # bits/code */
maxcode = MAXCODE(INIT_BITS); /* maximum code, given n_bits */
maxmaxcode = 1 << BITS; /* should NEVER generate this code */
free_ent = ((block_compress) ? FIRST : 256 );
ent = ngetchar();
hshift = 0;
for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L )
hshift++;
hshift = 8 - hshift; /* set hash code range bound */
hsize_reg = hsize;
cl_hash((count_int)hsize_reg); /* clear hash table */
while ((c = ngetchar()) != EOF) {
in_count++;
fcode = (long) (((long) c << maxbits) + ent);
i = ((c << hshift) ^ ent); /* xor hashing */
if (htabof (i) == fcode) {
ent = codetabof(i);
continue;
} else if ((long)htabof (i) < 0) /* empty slot */
goto nomatch;
disp = hsize_reg - i; /* secondary hash (after G. Knott) */
if (i == 0)
disp = 1;
probe:
if ((i -= disp) < 0)
i += hsize_reg;
if (htabof (i) == fcode) {
ent = codetabof(i);
continue;
}
if ((long)htabof (i) > 0)
goto probe;
nomatch:
output ((code_int) ent);
ent = c;
if (free_ent < maxmaxcode) {
codetabof(i) = free_ent++; /* code -> hashtable */
htabof(i) = fcode;
}
else if ((count_int)in_count >= checkpoint && block_compress)
cl_block ();
}
/*
* Put out the final code.
*/
output((code_int)ent);
output((code_int)-1);
return(CLen < fsize);
}
static char buf[BITS];
char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
output( code )
code_int code;
{
register int r_off = offset, bits= n_bits;
register char * bp = buf;
if ( code >= 0 ) {
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/*
* Since code is always >= 8 bits, only need to mask the first
* hunk on the left.
*/
*bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off];
bp++;
bits -= (8 - r_off);
code >>= 8 - r_off;
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
if ( bits >= 8 ) {
*bp++ = code;
code >>= 8;
bits -= 8;
}
/* Last bits. */
if(bits)
*bp = code;
offset += n_bits;
if (offset == (n_bits << 3)) {
bp = buf;
bits = n_bits;
mwrite(bp, bits);
bp += bits;
bits = 0;
offset = 0;
}
/*
* If the next entry is going to be too big for the code size,
* then increase it, if possible.
*/
if (free_ent > maxcode || (clear_flg > 0)) {
/*
* Write the whole buffer, because the input side won't
* discover the size increase until after it has read it.
*/
if (offset > 0)
mwrite(buf, n_bits);
offset = 0;
if (clear_flg) {
n_bits = INIT_BITS;
maxcode = MAXCODE(INIT_BITS);
clear_flg = 0;
} else {
n_bits++;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
} else {
/*
* At EOF, write the rest of the buffer.
*/
if (offset > 0)
mwrite(buf, (offset + 7) / 8);
offset = 0;
}
}
char *
xrindex(s, c) /* For those who don't have it in libc.a */
register char *s, c;
{
char *p;
for (p = NULL; *s; s++) {
if (*s == c)
p = s;
}
return(p);
}
cl_block() /* table clear for block compress */
{
register long int rat;
checkpoint = in_count + CHECK_GAP;
if (in_count > 0x007fffff) { /* shift will overflow */
rat = CLen >> 8;
if (rat == 0) { /* Don't divide by zero */
rat = 0x7fffffff;
} else {
rat = in_count / rat;
}
} else {
rat = (in_count << 8) / CLen; /* 8 fractional bits */
}
if (rat > ratio) {
ratio = rat;
} else {
ratio = 0;
cl_hash ( (count_int) hsize );
free_ent = FIRST;
clear_flg = 1;
output ( (code_int) CLEAR );
}
}
cl_hash(hsize) /* reset code table */
register count_int hsize;
{
register count_int *htab_p = htab+hsize;
register long i;
register long m1 = -1;
i = hsize - 16;
do { /* might use Sys V memset(3) here */
*(htab_p-16) = m1;
*(htab_p-15) = m1;
*(htab_p-14) = m1;
*(htab_p-13) = m1;
*(htab_p-12) = m1;
*(htab_p-11) = m1;
*(htab_p-10) = m1;
*(htab_p-9) = m1;
*(htab_p-8) = m1;
*(htab_p-7) = m1;
*(htab_p-6) = m1;
*(htab_p-5) = m1;
*(htab_p-4) = m1;
*(htab_p-3) = m1;
*(htab_p-2) = m1;
*(htab_p-1) = m1;
htab_p -= 16;
} while ((i -= 16) >= 0);
for ( i += 16; i > 0; i-- )
*--htab_p = m1;
}
UnCompressFile(insize)
{
register char_type *stackp;
register int finchar;
register code_int code, oldcode, incode;
/*
* As above, initialize the first 256 entries in the table.
*/
bzero(htab, sizeof(htab));
bzero(codetab, sizeof(codetab));
offset = clear_flg = ratio = 0;
in_count = 1;
checkpoint = CHECK_GAP;
n_bits = INIT_BITS; /* number of bits/code */
maxbits = BITS; /* user settable max # bits/code */
maxcode = MAXCODE(INIT_BITS); /* maximum code, given n_bits */
maxmaxcode = 1 << BITS; /* should NEVER generate this code */
for ( code = 255; code >= 0; code-- ) {
tab_prefixof(code) = 0;
tab_suffixof(code) = (char_type)code;
}
free_ent = ((block_compress) ? FIRST : 256 );
finchar = oldcode = getcode();
if (oldcode == -1) /* EOF already? */
return; /* Get out of here */
oputc((char)finchar); /* first code must be 8 bits = char */
stackp = de_stack;
while ((code = getcode()) > -1) {
if ((code == CLEAR) && block_compress) {
for (code = 255; code >= 0; code--)
tab_prefixof(code) = 0;
clear_flg = 1;
free_ent = FIRST - 1;
if ((code = getcode()) == -1) /* O, untimely death! */
break;
}
incode = code;
/*
* Special case for KwKwK string.
*/
if (code >= free_ent) {
*stackp++ = finchar;
code = oldcode;
}
/*
* Generate output characters in reverse order
*/
while ( code >= 256 ) {
*stackp++ = tab_suffixof(code);
code = tab_prefixof(code);
}
*stackp++ = finchar = tab_suffixof(code);
/*
* And put them out in forward order
*/
do
oputc (*--stackp);
while (stackp > de_stack);
/*
* Generate the new entry.
*/
if ((code=free_ent) < maxmaxcode) {
tab_prefixof(code) = (unsigned short)oldcode;
tab_suffixof(code) = finchar;
free_ent = code+1;
}
/*
* Remember previous code.
*/
oldcode = incode;
}
}
code_int
getcode()
{
/*
* On the VAX, it is important to have the register declarations
* in exactly the order given, or the asm will break.
*/
register code_int code;
static int offset = 0, size = 0;
static char_type buf[BITS];
register int r_off, bits;
register char_type *bp = buf;
if (clear_flg > 0 || offset >= size || free_ent > maxcode) {
/*
* If the next entry will be too big for the current code
* size, then we must increase the size. This implies reading
* a new buffer full, too.
*/
if ( free_ent > maxcode ) {
n_bits++;
if ( n_bits == maxbits )
maxcode = maxmaxcode; /* won't get any bigger now */
else
maxcode = MAXCODE(n_bits);
}
if ( clear_flg > 0) {
maxcode = MAXCODE (n_bits = INIT_BITS);
clear_flg = 0;
}
size = oread(buf, n_bits);
if (size <= 0)
return -1; /* end of file */
offset = 0;
size = (size << 3) - (n_bits - 1);
}
r_off = offset;
bits = n_bits;
/*
* Get to the first byte.
*/
bp += (r_off >> 3);
r_off &= 7;
/* Get first part (low order bits) */
code = (*bp++ >> r_off);
bits -= (8 - r_off);
r_off = 8 - r_off; /* now, offset into code word */
/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
if ( bits >= 8 ) {
code |= *bp++ << r_off;
r_off += 8;
bits -= 8;
}
/* high order bits. */
code |= (*bp & rmask[bits]) << r_off;
offset += n_bits;
return code;
}
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Makefile for backup
SYMS= include:symbols.m
SYMC= include:local/makesymbols.c
CFLAGS= +L +I$(SYMS)
LFLAGS= +Q
DD= srcc:
OD= T:
$(DD)backup: backup.c
cc $(CFLAGS) backup.c -o $(OD)backup.o
ln +Q $(OD)backup.o -lsup32 -lc32 -o $(DD)backup
delete $(OD)backup.o
$(SYMS): $(SYMC)
make -f include:local/Makefile
SHAR_EOF
cat << \SHAR_EOF > backup.doc
BACKUP.DOC
V2.01
3 November 1988
(c)Copyright 1988, Matthew Dillon, All Rights Reserved
Freely Distributable for non-profit only
(this is the same document as backup.doc v2.00)
(I)
OVERVIEW
Backup and Restore (same executable, just renamed) allow you to backup
any directory tree with optional compression, and later extract all
or part of the tree. The protection, date, and file comment is saved
with each file.
SEE SECTION (6), FLOPICAL BACKUP, for options pertaining to backing
up directly to floppies.
LIMITATIONS: When using -c (compress), the entire result of the
compressed file must fit into memory. Any files (after compression,
if any) larger than a floppy when backing up to floppies cannot be
handled yet.
(I)
INSTALLATION
Copy the (1) executable to your C: directory twice, naming one
'Backup' and the other 'Restore'. The program looks at the first
character of its name to determine whether the default is to backup
or restore. If you don't have the space, all is not lost ... you
simply need to specify backup or restore explicitly with the options
(-b or -r).
(2)
EXOTIC FEATURES
Backup does not write directory trees which contain no files. Thus,
when doing incremental backups on a large hard disk, the structure of
directory sub-trees which have no new files in them will not be
written. So if you only modified a couple of files since the last
backup, there won't be 50K of structure-overhead.
You can specify compression (-c) in the backup. All files are
compressed (there must be enough RAM to hold the largest file in
its compressed state). Those files which would turn out larger after
compression than before are NOT compressed. Certain file suffixes
cause the compression attempt to be skipped entire: .Z, .ARC, and .ZOO
files
IT IS SUGGESTED YOU USE THE COMPRESSION FEATURE EVEN THOUGH IT
SLOWS DOWN THE BACKUP. The resulting backup file will be
significantly reduced in size. Even though a full backup will
take a long time, you only do that every once in a while and
as incremental backups are shorter, they take much less time.
'Pick' patterns are supported (backup only paths which match the
specified patterns), and 'Discard' patterns are supported (do not backup
paths which match the specified patterns). Discard patterns override
Pick patterns.
(3)
OPTIONS
Here is a list of Backup/Restore options.
-A ARCHIVE. (Backup) Causes the archive bit to be cleared
on files being backed up. (Protection bit -> 1)
(Restore): If specified, the archive bit is cleared on
restored files. Otherwise the archive bit will be set,
causing the files to be backed up again on the next
incremental backup.
-U UPDATE. (Backup) Restricts the backup to only those files
which have the archive bit set. (Protection bit == 0)
-b BACKUP (this is the default if the first character in
the executable is a 'b')
-r RESTORE (this is the default if the first character in
the executable is an 'r')
-a APPEND (Backup) Cause the backup to be appended to
the specified (-o) file rather than overwriting it. Useful
for incremental backups.
-c COMPRESS (Backup) Cause all files to be compressed, if
possible. Restore automatically decompresses files which
have been compressed. This is the same algorithm used
in the UNIX compress command.
NO COMPRESSION WILL EVEN BE ATTEMPED ON FILES WITH THE
FOLLOWING SUFFIXES: .Z .ARC .ZOO
-f[#kb] Turn on backup fragmentation and specify the fragment size,
default 800K (for backing up to floppies). NOTE: Currently,
files larger than the fragment size cannot be backed up!
Note: a suffix is added to the file name w/ a sequence
number, but each fragment is a complete backup file unto
itself and may be restored out of order.
Any numerical value specified is in K, i.e. -f800 == 800K
-Fvol: Add the volume (df0:, df1:, etc..) to the drive sequencing
list. The idea is to specify several -F options which are
cycled through and prefix each fragment. This also
enables prompting... the program prompts whenever it
switches to the next fragment.
If -F is not specified at all and -f is, no prompting
occurs.
-s Only display directories as we go along
-S Silent... don't display files or directories
-v Verbose... display those files which will NOT be backed
up as well as those that will.
-l/-t (both do the same thing). (Restore) LIST the archive
only, do not do an actual restore.
-T (Restore) Restore ONLY the timestamp, comment, and
protection fields. This is incredibly useful to
restore timestamp information to files that already
exist. NO files are overwritten or created on the
destination volume(s).
-n# (Backup) Sets the output buffer size, in KILOBYTES.
Default is 64.
-pPAT (Backup) Restrict the backup to files which match the
specified pattern. Up to 32 patterns may be specified.
(Restore) Extract only those files which match the
specified pattern.
NOTE: The pattern is matched with the COMPLETE PATH as
shown in the -t option. '*' and '?' wildcarding is supported
-dPAT (Backup) Do not backup files which match the specified
pattern. Up to 32 patterns may be specified.
(Restore) Do not extract files which match the specified
pattern.
NOTE: The pattern is matched with the COMPLETE PATH as
shown in the -t option. '*' and '?' wildcarding is supported.
-oFILE (Backup) Specify the OUTPUT FILE for a backup. IF NO
OUTPUT FILE IS SPECIFIED, NO OUTPUT FILE IS CREATED. I.E.
you can take a second pass just to clear the archive bit.
(Restore) Specify the directory to place the restored
directory tree. (This overrides the volume the backup
was saved from). If no directory specified, the original
volume used in the backup will be restored to. If no
volume was specified in the backup but rather a relative
path was, the restore will be relative to the current
directory.
(4)
STANDARD METHOD OF BACKING UP
Note that with combinations of -o, -A, and -U, you have many choices
available to you when backing up files. For hard disks, backups are
normally done in two phases:
-Once a month do a complete backup of your hard disk
-Every day do a much smaller 'incremental' backup which backs
up only those files which have been modified/created since the
last backup.
The master backup, plus the N incremental backups together
reconstruct your disk up to the moment. After a while the
number of incremental backups will be so great (and messy),
that you will want to do another complete backup and start
again.
IN THIS WAY, the COMPRESS option can be put to good use. The
incremental backups are usually small, and can be compressed
relatively quickly. The complete backup should also be compressed
(-c in case you forgot) though this will take a much longer time...
but then again the complete backup is not done every day.
UPDATING THE ARCHIVE BIT ON THE FLY VERSES TAKING A SECOND PASS.
Updating the archive bit involves writing to the disk. For complete
safety you probably do NOT want to update the archive bit while you
are backing up the files in question. The following two-pass method
works well:
(COMPLETE BACKUP)
backup -c -d"*.o" volume: -ooutput
backup -A volume:
(INCREMENTAL BACKUP)
backup -U -c -d"*.o" volume: -ooutput
backup -A volume:
The -d option is saying "Don't backup all those object modules I left
laying around".
The first command backs up only those files which have be modified
or created but does NOT update the archive bit. The second command
updates the archive bit without doing anything else (no output file
is specified and thus none is created). The second command goes
extremely quickly since files are not actually read.
For your INCREMENTAL backups, risking modifying the archive bit while
backing the files up is feasible, Since very few files will be
backed up (comparitively):
backup -a -UA -c -d"*.o" volume: -ooutput
In this case I use the -a option (append). You might NOT want to do
this and save the incremental backups as separate files.
(5)
STANDARD METHOD OF RESTORING
Restoring a backup to the original volume: (Restore or Backup -r)
NOTE: If the -A option is specified, restored files will have
their Archive bit cleared. If -A is NOT specified, files will have
their archive bit set and will thus be re-backed up on the next
incremental backup.
NOTE: When restoring files, remember that the incremental backups
will have the newest modifications and should be specified LAST:
Restore completebkfile incr1 incr2 incr3....
Partial Restore using -p:
Restore -pstuffIWant bkfile1 bkfile2 bkfile3...
ARCHIVE LISTING:
Restore -t bkfile1 bkfile2....
NOTE: Two 'sizes' are given in the listing. The first
is the # bytes the file takes up in the archive, the
second is the actual size of the file.
(6)
FLOPICAL BACKUP AND RESTORE
There are two problems associated with floppy backup and restore:
(1) A single file on the HD may be two big for a single floppy
(2) It may take a lot of floppies to backup an HD
Additionaly, one wants the following conveniences:
(1) Be able to backup to a logical progression of (floppy) drives,
inserted blank disks in while the system is working on other
drives.
(2) Be able to restore the same way
(3) If one or more floppies is destroyed be able to restore from the
remaining floppies.
Currently, you can specify which devices to cycle through
with the -F option (-Fdf0: -Fdf1: -Fdf2:), but BACKUP will
always prompt you before continuing. Since it uses the
console device, you can specify the yes response as many
times as you have floppies in ready drives.
Currently, files larger than the specified -f size (800K
default) cannot be handled if using -f/-F ... However, by
enabling compression larger files can still fit.
Currently, the entire compressed result of a file must be
able to fit into memory during the backup.
***
BACKUP. Specify the -f option and then -Fvol: for each floppy
drive (in the order you want) to backup to. Example:
-f -Fdf0: -Fdf1:
(Also keep in mind all the options listed in sections (3), (4), & (5))
Currently, you must have PRE-FORMATTED all your backup disks since the
backup/restore works through normal files. The system will prompt for
readyness every time it completes a section.
The backup process never deletes files, only creates or overwrites
them. Thus, accidently sticking in a non-blank or incorrect backup
disk will probably result in a disk-full requester.... not Fatal, though
inconvenient.
You must still specify the -o option to name the output file, which will
be prefixed with the next -F volume in sequence and suffixed with the
sequence number. (.nn) The disk labels for your floppies may all be
the same.
EXAMPLE:
backup -c -d"*.o" -f -Fdf0: -Fdf1: -Fdf2: volume: -oxxbak
backup -A volume:
NOTE: You do NOT specifiy a volume prefix in the -o option ..
those specified by -F are automatically cycled through as the
prefix.
* If your hard disk has enough space on it I suggest you backup to an
alternate partition using only -f (not -F which forces user prompts),
then copy the chunks to floppies after the entire backup has
completed.
***
RESTORE. Restore works the same way. EACH DISK IS INDEPENDANT! You
may insert disks in any order to be restored. Simply execute the
standard restore command in section (5) for each floppy.
Destroyed disks may be skipped. Currently, no support for partially
restored disks exists... i.e. the archive has be clean.
SHAR_EOF
# End of shell archive
exit 0
--
Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page
Have five nice days.